﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SPC
{
    public interface ISPC
    {        
        double LSL { set; }
        double USL { set; }
        SCtrlDiagram[] GetCtrlDiagrams();
        double CP();
        double CPK();
        double PP();
        double PPK();
    }
    public class SPControl
    {
#region 变量
        double usl=0;
        double lsl=0;
        double cpkLimit = 1.33;//计数型控制图，cpk值是不良品率或缺陷率。
        ECtrlChartType ctrlChartType = ECtrlChartType.Xbar_Range;
        List<SUnormals> unormList = new List<SUnormals>();
        ISPC spc;
        #endregion 变量
        #region 属性
        public double USL
        { set
            {
                usl = value;
                spc.USL = usl;
            } }
        public double LSL {
            set
            {
                lsl = value;
                spc.LSL = lsl;
            } }
        public double CPKLimit { set { cpkLimit = value; } }
        
        public ECtrlChartType CtrlChartType
        {
            set
            {
                ctrlChartType = value;        
            }
        }
        #endregion 属性
        
        public SPControl(double[] x, double usl, double lsl, ECtrlChartType ctrlChartType = ECtrlChartType.X_Rm)
        {
            this.usl = usl;
            this.lsl = lsl;
            this.ctrlChartType = ctrlChartType;
            if(ctrlChartType==ECtrlChartType.X_Rm)
            {
                spc = new SpcXRm(x, usl, lsl);
            }
            
            else if (ctrlChartType == ECtrlChartType.Cchart)
            {
                spc = new SpcC(x);
            }
            AddUnormalDescription();
        }
        public SPControl(double[][] x,double usl,double lsl,ECtrlChartType ctrlChartType=ECtrlChartType.Xbar_Range)
        {
            this.usl = usl;
            this.lsl = lsl;
            this.ctrlChartType = ctrlChartType;
            if (ctrlChartType == ECtrlChartType.Xbar_Range)
            {
                spc = new SpcXbarR(x, usl, lsl);
            }
            else if(ctrlChartType == ECtrlChartType.Xbar_S)
            {
                spc = new SpcXbarS(x, usl, lsl);
            }
            else if(ctrlChartType==ECtrlChartType.Pchart)
            {
                double[] samples = new double[x.Length];
                double[] NGs=new double[x.Length];
                for (int i = 0; i < x.Length; i++)
                {
                    samples[i] = x[i][0];
                    NGs[i] = x[i][1];
                }
                spc = new SpcP(samples, NGs);
                
            }
            else if(ctrlChartType==ECtrlChartType.Uchart)
            {
                double[] samples = new double[x.Length];
                double[] NGs = new double[x.Length];
                for (int i = 0; i < x.Length; i++)
                {
                    samples[i] = x[i][0];
                    NGs[i] = x[i][1];
                }
                spc = new SpcU(samples, NGs);
            }
            else if (ctrlChartType == ECtrlChartType.nPchart)
            {
                int samples =(int)Math.Round( x[0][0],0);
                double[] NGs = new double[x.Length];
                for (int i = 0; i < x.Length; i++)
                {
                    NGs[i] = x[i][1];
                }
                spc = new SpcNP(samples, NGs);
            }
            AddUnormalDescription();
        }
        public SCtrlDiagram[] GetCtrlDiagrams() { return spc.GetCtrlDiagrams(); }
        public double CP() { return spc.CP(); }
        /// <summary>
        /// 返回CPK值或不合格率（缺陷率）
        /// </summary>
        /// <returns>计数型图：不合格率（缺陷率），计量型：CPK</returns>
        public double CPK() { return spc.CPK(); }
        public double PP() { return spc.PP(); }
        public double PPK() { return spc.PPK(); }
        public bool IsCpkPass()
        {
            if(ctrlChartType==ECtrlChartType.Xbar_Range
                || ctrlChartType == ECtrlChartType.Xbar_S
                || ctrlChartType == ECtrlChartType.X_Rm)
            { return CPK() >= cpkLimit; }
            else
            { return CPK() <= cpkLimit; }
        }
        

        public List<SUnormals> GetUnormals()
        {
            List<SUnormals> unormList = new List<SUnormals>();
            SCtrlDiagram[] diags = GetCtrlDiagrams();
            for (int i = 0; i < diags.Length; i++)
            {
                unormList.AddRange(FindUnNormals(diags[i]));
            }
            return unormList;
        }
        /// <summary>
        /// 查找8类异常点
        /// </summary>
        /// <param name="diag.points">控制图数据，如Xbar, Range等</param>
        /// <param name="ucl"></param>
        /// <param name="lcl"></param>
        /// <param name="cl"></param>
        /// <param name="includeAllCharts">对所有控制图计算异常点？缺省只查找Xbar或X图的异常点</param>
        /// <returns></returns>
        private List<SUnormals> FindUnNormals(SCtrlDiagram diag, bool includeAllCharts=false)
        {
            List<SUnormals> unormList = new List<SUnormals>();
            if(!includeAllCharts)
            {
                if (diag.diagramName != DiagramName.X && diag.diagramName != DiagramName.xbar)
                {//判异准则仅针对X值或均值
                    return unormList;
                }
            }
            
            double sigma = (diag.ucl - diag.lcl) / 6;

            //第一类异常：超出控制线
            for (int k = 0; k < diag.points.Length; k++)
            {
                if (diag.points[k] > diag.ucl || diag.points[k] < diag.lcl)
                {
                    SUnormals unormal = new SUnormals()
                    {
                        pointIndex = k,
                        UnNormType = EUnormals.Out_Of_ControlLine
                    };
                    unormList.Add(unormal);
                }
            }
            //第二类异常：连续9点在同一向
            List<int> unmPoints = new List<int>();
            for (int k = 0; k < diag.points.Length; k++)
            {
                if (k == 0)
                {
                    unmPoints.Add(k);
                }
                else
                {
                    if ((diag.points[k] - diag.cl) * (diag.points[k - 1] - diag.cl) < 0) //异向
                    {
                        if (unmPoints.Count >= 9) //第二类异常
                        {
                            foreach (int pind in unmPoints)
                            {
                                SUnormals unormal = new SUnormals()
                                {
                                    pointIndex = pind,
                                    UnNormType = EUnormals._9Points_Continous_One_side
                                };
                                unormList.Add(unormal);
                            }
                        }
                        unmPoints.Clear();//清空后重新累计
                    }
                    unmPoints.Add(k);
                }
            }
            if (unmPoints.Count >= 9) //第二类异常
            {
                foreach (int pind in unmPoints)
                {
                    SUnormals unormal = new SUnormals()
                    {
                        pointIndex = pind,
                        UnNormType = EUnormals._9Points_Continous_One_side
                    };
                    unormList.Add(unormal);
                }
            }
            //三类异常：连续6点递增或递减
            unmPoints.Clear();
            for (int i = 0; i < diag.points.Length; i++)
            {
                if (i < 2) unmPoints.Add(i);
                else
                {
                    if ((diag.points[i] - diag.points[i - 1]) * (diag.points[i - 1] - diag.points[i - 2]) < 0)
                    {
                        if (unmPoints.Count >= 6) //三类异常已发生
                        {
                            foreach (int pind in unmPoints)
                            {
                                SUnormals unormal = new SUnormals()
                                {
                                    pointIndex = pind,
                                    UnNormType = EUnormals._6Points_Continous_Increase_Or_Decreas
                                };
                                unormList.Add(unormal);
                            }
                        }
                        unmPoints.Clear();
                        unmPoints.Add(i - 1);
                    }
                    unmPoints.Add(i);
                }
            }
            if (unmPoints.Count >= 6) //三类异常发生
            {
                foreach (int pind in unmPoints)
                {
                    SUnormals unormal = new SUnormals()
                    {
                        pointIndex = pind,
                        UnNormType = EUnormals._6Points_Continous_Increase_Or_Decreas
                    };
                    unormList.Add(unormal);
                }
            }
            unmPoints.Clear();//四类异常：连续14点连续交叉分布
            for (int i = 0; i < diag.points.Length; i++)
            {
                if (i < 2) unmPoints.Add(i);
                else
                {
                    if ((diag.points[i] - diag.points[i - 1]) * (diag.points[i - 1] - diag.points[i - 2]) > 0)
                    {//交替终止
                        if (unmPoints.Count >= 14) //三类异常发生
                        {
                            foreach (int pind in unmPoints)
                            {
                                SUnormals unormal = new SUnormals()
                                {
                                    pointIndex = pind,
                                    UnNormType = EUnormals._14Points_Sawtooth
                                };
                                unormList.Add(unormal);
                            }
                        }
                        unmPoints.Clear();
                        unmPoints.Add(i - 1);
                    }
                    unmPoints.Add(i);
                }
            }
            if (unmPoints.Count >= 14) //4类异常发生
            {
                foreach (int pind in unmPoints)
                {
                    SUnormals unormal = new SUnormals()
                    {
                        pointIndex = pind,
                        UnNormType = EUnormals._14Points_Sawtooth
                    };
                    unormList.Add(unormal);
                }
            }
            unmPoints.Clear();//五类异常：连续3点中有两点超B线
            for (int i = 2; i < diag.points.Length; i++)
            {
                int count = 0;
                for (int k = i - 2; k <= i; k++)
                {
                    if (diag.points[k] > diag.cl + 2 * sigma)
                    {
                        count++;
                    }
                }
                if (count >= 2)
                {
                    unmPoints.Add(i - 2);
                    unmPoints.Add(i - 1);
                    unmPoints.Add(i);
                }
                count = 0;
                for (int k = i - 2; k <= i; k++)
                {
                    if (diag.points[k] < diag.cl - 2 * sigma)
                    {
                        count++;
                    }
                }
                if (count >= 2)
                {
                    unmPoints.Add(i - 2);
                    unmPoints.Add(i - 1);
                    unmPoints.Add(i);
                }
            }
            if (unmPoints.Count > 0)
            {
                int[] inds = (from n in unmPoints
                              select n).Distinct().ToArray();
                SUnormals un = new SUnormals()
                {
                    UnNormType = EUnormals._2of3Continous_points_Out_Of_B_Area
                };
                foreach (int n in inds)
                {
                    un.pointIndex = n;
                    unormList.Add(un);
                }
            }

            unmPoints.Clear();//异常6：连续5点中有4点在同侧C区之外 
            for (int i = 4; i < diag.points.Length; i++)
            {
                int count = 0;
                for (int k = i - 4; k <= i; k++)
                {
                    if (diag.points[k] > diag.cl + 2 * sigma) count++;
                }
                if (count >= 4)
                {
                    unmPoints.Add(i - 4);
                    unmPoints.Add(i - 3);
                    unmPoints.Add(i - 2);
                    unmPoints.Add(i - 1);
                    unmPoints.Add(i);
                }
                count = 0;
                for (int k = i - 4; k <= i; k++)
                {
                    if (diag.points[k] < diag.cl - 2 * sigma) count++;
                }
                if (count >= 4)
                {
                    unmPoints.Add(i - 4);
                    unmPoints.Add(i - 3);
                    unmPoints.Add(i - 2);
                    unmPoints.Add(i - 1);
                    unmPoints.Add(i);
                }

            }
            if (unmPoints.Count > 0)
            {
                int[] inds = (from n in unmPoints
                              select n).Distinct().ToArray();
                SUnormals un = new SUnormals()
                {
                    UnNormType = EUnormals._4of5Continous_points_Out_Of_C_Area
                };
                foreach (int n in inds)
                {
                    un.pointIndex = n;
                    unormList.Add(un);
                }
            }
            //连续15点在C区以内                 
            unmPoints.Clear();
            for (int i = 0; i < diag.points.Length; i++)
            {
                if (diag.points[i] > diag.cl + sigma || diag.points[i] < diag.cl - sigma)
                {
                    if (unmPoints.Count >= 15)
                    {
                        SUnormals un = new SUnormals()
                        {
                            UnNormType = EUnormals._15Points_Continous_Inside_C_Area
                        };
                        for (int k = 0; k < unmPoints.Count; k++)
                        {
                            un.pointIndex = unmPoints[k];
                            unormList.Add(un);
                        }
                    }
                    unmPoints.Clear();
                }
                else
                    unmPoints.Add(i);
            }
            if (unmPoints.Count >= 15)
            {
                if (unmPoints.Count >= 15)
                {
                    SUnormals un = new SUnormals()
                    {
                        UnNormType = EUnormals._15Points_Continous_Inside_C_Area
                    };
                    for (int k = 0; k < unmPoints.Count; k++)
                    {
                        un.pointIndex = unmPoints[k];
                        unormList.Add(un);
                    }
                }
            }
            //连续8点落在中心线两侧，但无1点落在C区内
            unmPoints.Clear();
            for (int i = 0; i < diag.points.Length; i++)
            {
                if (diag.points[i] < diag.cl + sigma || diag.points[i] > diag.cl - sigma)
                {
                    if (unmPoints.Count >= 8)
                    {
                        SUnormals un = new SUnormals()
                        {
                            UnNormType = EUnormals._8Points_Continous_Biside_But_Out_Of_C_Area
                        };
                        for (int k = 0; k < unmPoints.Count; k++)
                        {
                            un.pointIndex = unmPoints[k];
                            unormList.Add(un);
                        }
                    }
                    unmPoints.Clear();
                }
                else unmPoints.Add(i);
            }
            if (unmPoints.Count >= 8)
            {
                SUnormals un = new SUnormals()
                {
                    UnNormType = EUnormals._8Points_Continous_Biside_But_Out_Of_C_Area
                };
                for (int k = 0; k < unmPoints.Count; k++)
                {
                    un.pointIndex = unmPoints[k];
                    unormList.Add(un);
                }
            }
            return unormList;
        }
        public string GetUnormalMessege(EUnormals unormalType)
        {            
            return dUnormals[unormalType];
        }
        Dictionary<EUnormals, string> dUnormals = new Dictionary<EUnormals, string>();
        private void AddUnormalDescription()
        {
            string desc = "异常现象：任意一点超出控制限。\r\n异常原因：在这一点上出现了特殊原因。";
            desc += "\r\n对策：应该排查原因，如不合格原材料,设备的不正确调整，刀具的严重磨损，操作员偏离操作规程等。";
            dUnormals.Add(EUnormals.Out_Of_ControlLine, desc);
            desc = "异常现象：连续9点在中心线一侧。";
            desc += "\r\n异常原因：中心值偏移";
            dUnormals.Add(EUnormals._9Points_Continous_One_side, desc);
            desc = "异常现象：连续6点递增或递减。";
            desc += "\r\n异常原因：工具逐渐磨损、维修逐渐变坏";            
            dUnormals.Add(EUnormals._6Points_Continous_Increase_Or_Decreas, desc);
            desc = "异常现象：连续14点相邻点上下交替。";
            desc += "\r\n异常原因：轮换使用两台设备？两个作业人员轮换操作？";
            dUnormals.Add(EUnormals._14Points_Sawtooth, desc);
            desc = "异常现象：连续3点中有2点在同侧B区之外。";
            desc += "\r\n异常原因：均值发生了变化";
            dUnormals.Add(EUnormals._2of3Continous_points_Out_Of_B_Area, desc);
            desc = "异常现象：连续5点中有4点在同侧C区之外。";
            desc += "\r\n异常原因：均值发生了变化";
            dUnormals.Add(EUnormals._4of5Continous_points_Out_Of_C_Area, desc);
            desc = "异常现象：连续15点在C区以内。";
            desc += "\r\n异常原因：人为处理数据？如两条产线的产品混在一起？如果是控制用图，说明控制参数已经久未调整，该修订控制限了";
            dUnormals.Add(EUnormals._15Points_Continous_Inside_C_Area, desc);
            desc = "异常现象：连续8点落在中心线两侧，但无1点落在C区内。";
            desc += "\r\n异常原因：数据分层不够，如两条产线的产品混在一起";
            dUnormals.Add(EUnormals._8Points_Continous_Biside_But_Out_Of_C_Area, desc);
        }
    }
    public class CtrlChartFactor
    {
        static DataTable factorTable=InitialFactorTable();
        public static double Factor(int groupCapacity, string name)
        {
            if (groupCapacity == 0)
            {
                throw (new Exception("发现子组容量为0，数据未初始化或子组数据量不相同？"));
            }
            int rowIndex = groupCapacity - 2;
            return double.Parse(factorTable.Rows[rowIndex][name].ToString());
        }
        private static DataTable InitialFactorTable()
        {
            DataTable d = new DataTable();
            string[] titles = { "n", "A2", "d2", "D3", "D4", "A3", "c4", "B3", "B4", "E2" };
            for (int i = 0; i < titles.Length; i++)
            {
                d.Columns.Add(titles[i]);
            }
            d.Rows.Add(2, 1.88, 1.128, 0, 3.267, 2.659, 0.7979, 0, 3.267, 2.66);
            d.Rows.Add(3, 1.023, 1.693, 0, 2.574, 1.954, 0.8862, 0, 2.568, 1.772);
            d.Rows.Add(4, 0.729, 2.059, 0, 2.282, 1.628, 0.9213, 0, 2.266, 1.457);
            d.Rows.Add(5, 0.577, 2.326, 0, 2.114, 1.427, 0.94, 0, 2.089, 1.29);
            d.Rows.Add(6, 0.483, 2.534, 0, 2.004, 1.287, 0.9515, 0.03, 1.97, 1.184);
            d.Rows.Add(7, 0.149, 2.704, 0.076, 1.924, 1.182, 0.9594, 0.118, 1.882, 1.109);
            d.Rows.Add(8, 0.373, 2.847, 0.136, 1.864, 1.099, 0.965, 0.185, 1.815, 1.054);
            d.Rows.Add(9, 0.337, 2.97, 0.184, 1.816, 1.032, 0.9693, 0.239, 1.761, 1.01);
            d.Rows.Add(10, 0.308, 3.078, 0.223, 1.777, 0.975, 0.9727, 0.284, 1.716, 0.975);
            d.Rows.Add(11, 0.285, 3.173, 0.256, 1.744, 0.927, 0.9754, 0.321, 1.679);
            d.Rows.Add(12, 0.266, 3.258, 0.283, 1.717, 0.886, 0.9776, 0.354, 1.646);
            d.Rows.Add(13, 0.249, 3.336, 0.307, 1.693, 0.85, 0.9794, 0.382, 1.618);
            d.Rows.Add(14, 0.235, 3.407, 0.328, 1.672, 0.817, 0.981, 0.406, 1.594);
            d.Rows.Add(15, 0.223, 3.472, 0.347, 1.653, 0.789, 0.823, 0.428, 1.572);
            d.Rows.Add(16, 0.212, 3.532, 0.363, 1.637, 0.763, 0.9835, 0.448, 1.552);
            d.Rows.Add(17, 0.203, 3.588, 0.738, 1.622, 0.739, 0.9845, 0.466, 1.534);
            d.Rows.Add(18, 0.194, 3.64, 0.391, 1.608, 0.718, 0.9854, 0.482, 1.518);
            d.Rows.Add(19, 0.187, 3.689, 0.403, 1.597, 0.698, 0.9862, 0.497, 1.503);
            d.Rows.Add(20, 0.18, 3.735, 0.415, 1.585, 0.68, 0.9869, 0.51, 1.49);
            d.Rows.Add(21, 0.173, 3.778, 0.425, 1.575, 0.663, 0.9876, 0.523, 1.477);
            d.Rows.Add(22, 0.167, 3.819, 0.434, 1.566, 0.647, 0.9882, 0.534, 1.466);
            d.Rows.Add(23, 0.162, 3.858, 0.443, 1.557, 0.633, 0.9887, 0.545, 1.455);
            d.Rows.Add(24, 0.157, 3.895, 0.451, 1.548, 0.619, 0.9892, 0.555, 1.445);
            d.Rows.Add(25, 0.153, 3.931, 0.459, 1.541, 0.606, 0.9893, 0.565, 1.435);
            return d;
        }
    }
    public struct SUnormals
    {
        public int pointIndex;
        public EUnormals UnNormType;
    }

    public enum EUnormals
    {
        Out_Of_ControlLine,//落入控制线以外的点
        _9Points_Continous_One_side,//连续9点在中心线一侧
        _6Points_Continous_Increase_Or_Decreas,//连续6点递增或递减
        _14Points_Sawtooth,//连续14点相邻点上下交替，如锯齿般
        _2of3Continous_points_Out_Of_B_Area,//连续3点中有2点在同侧B区之外
        _4of5Continous_points_Out_Of_C_Area,//连续5点中有4点在同侧C区之外
        _15Points_Continous_Inside_C_Area,//连续15点在C区以内
        _8Points_Continous_Biside_But_Out_Of_C_Area//连续8点落在中心线两侧，但无1点落在C区内
    }
    public enum ECtrlChartType
    {
        Xbar_Range,Xbar_S,X_Rm,Pchart,nPchart,Cchart,Uchart
    }
    public  class DiagramName
    {//判异准则仅针对X值或均值
        public static string xbar = "Xbar";
        public static string range = "Range";
        public static string S  = "Sigma";
        public static string X = "X";
        public static string Rm = "Rm";
        public static string p  = "P";
        public static string np = "nP";
        public static string c = "C";
        public static string u = "U";
    }
    public class SCtrlDiagram
    {
        public string diagramName;
        public double ucl, cl, lcl;
        public double[] points;
        public double[] ucls=new double[0];
        public double[] lcls=new double[0];//多控制线时用。如果缺省不启用
        public SCtrlDiagram() { }
        public SCtrlDiagram(string diagramName, double ucl, double cl, double lcl, double[] points)
        {
            this.diagramName = diagramName;
            this.ucl = ucl;
            this.cl = cl;
            this.lcl = lcl;
            this.points = points;
        }
    }
}
